package cn.sdormitory.common.utils;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Assert;
import com.google.common.collect.Table;
import org.apache.commons.collections4.IterableUtils;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Array;
import java.util.*;
import java.util.function.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 集合工具类
 */
public class ContainerUtil {
    public static <T> boolean isEmpty(Table<?,?,?> table){
        return table == null || table.isEmpty();
    }

    public static <T> boolean isEmpty(Iterable<T> iterable) {
        return iterable == null || IterableUtils.isEmpty(iterable);
    }

    public static <T> boolean isEmpty(Collection<T> collection) {
        return CollectionUtils.isEmpty(collection);
    }

    public static <K,V> boolean isEmpty(Map<K,V> map) {
        return map == null || map.isEmpty();
    }

    public static <T> boolean isEmpty(T[] objs) {
        return objs == null || objs.length==0;
    }

    public static <K,V> boolean notEmpty(Map<K,V> map) {
        return map != null && !map.isEmpty();
    }

    public static <T> boolean notEmpty(Collection<T> collection) {
        return collection != null && collection.size() > 0;
    }

    public static <T> boolean notEmpty(T[] objs){
        return objs != null && objs.length > 0;
    }

    public static <T> int size(Collection<T> collection){
        return collection == null ? 0 : collection.size();
    }

    public static <K,V> int size(Map<K,V> map){
        return map == null ? 0 : map.size();
    }

    public static <T> int size(T[] array){
        return array == null ? 0 : array.length;
    }

    public static <T> List<T> handleNull(List<T> list){
        return list == null ? Collections.emptyList() : list;
    }

    public static <T> Set<T> handleNull(Set<T> set){
        return set == null ? Collections.emptySet() : set;
    }

    public static <K,V> Map<K,V> handleNull(Map<K,V> map){
        return map == null ? Collections.emptyMap() : map;
    }

    public static <T> Collection<T> handleNull(Collection<T> collection,Supplier<Collection<T>> emptyGenerator){
        return collection == null ? emptyGenerator.get() : collection;
    }

    public static <C extends Collection<T>,T> void ifCondition(boolean condition, C collection, Consumer<? super C> action) {
        if(condition){
            action.accept(collection);
        }
    }

    public static <C extends Collection<T>,T> void ifNotEmpty(C collection, Consumer<? super C> action) {
        if(ContainerUtil.isEmpty(collection)){
            return;
        }
        action.accept(collection);
    }

    public static <C extends Collection<T>,T> void ifIsEmpty(C collection, Consumer<? super C> action) {
        if(ContainerUtil.notEmpty(collection)){
            return;
        }
        action.accept(collection);
    }

    public static  <C extends Collection<T>,T> void ifIsNull(C collection, Consumer<? super C> action) {
        if(ContainerUtil.notEmpty(collection)){
            return;
        }
        action.accept(collection);
    }

    public static <K,V> Map<K,V> trimByNullKey(Map<K,V> map){
        return handleMapByFilter(map,entry->entry.getKey() != null);
    }

    public static <K,V> Map<K,V> trimByNullValue(Map<K,V> map){
        return handleMapByFilter(map,entry->entry.getValue() != null);
    }

    public static <K,V> Map<K,V> handleMapByFilter(Map<K,V> map, Predicate<Map.Entry<K,V>> predicate){
        if(ContainerUtil.isEmpty(map)){
            return map;
        }
        Iterator<Map.Entry<K, V>> iterator = map.entrySet().iterator();
        while (iterator.hasNext()){
            Map.Entry<K, V> entry = iterator.next();
            boolean filter = predicate.test(entry);
            if(!filter){
                iterator.remove();
            }
        }
        return map;
    }

    public static  <T> Stream<T> stream(Iterable<T> iterable){
        Collection<T> collection = toCollection(iterable);
        return isEmpty(collection) ? Stream.empty() : collection.stream();
    }

    public static <T> Stream<T> stream(T[] array){
        return isEmpty(array) ? Stream.empty() : Arrays.stream(array);
    }

    public static  <C extends Collection<T>,T> Stream<T> filter(C collection, Predicate<? super T> predicate){
        return stream(collection).filter(predicate);
    }

    public static <T> List<T> filterList(Collection<T> collection, Predicate<? super T> predicate){
        return stream(collection).filter(predicate).collect(Collectors.toList());
    }

    public static <T> Set<T> filterSet(Collection<T> collection, Predicate<? super T> predicate){
        return stream(collection).filter(predicate).collect(Collectors.toSet());
    }

    public static  <T> Stream<T> filter(T[] array, Predicate<? super T> predicate){
        return stream(array).filter(predicate);
    }

    public static  <T> List<T> filterList(T[] array, Predicate<? super T> predicate){
        return stream(array).filter(predicate).collect(Collectors.toList());
    }

    public static <K,V> Map<K,V> filter(Map<K,V> map, Predicate<Map.Entry<K,V>> predicate){
        if(isEmpty(map)){
            return Collections.emptyMap();
        }

        Map<K,V> result = new HashMap<>();
        Iterator<Map.Entry<K, V>> iterator = map.entrySet().iterator();
        while (iterator.hasNext()){
            Map.Entry<K, V> entry = iterator.next();
            boolean filter = predicate.test(entry);
            if(filter){
               result.put(entry.getKey(),entry.getValue());
            }
        }
        return result;
    }

    public static <T> List<T> limit(List<T> list, int maxSize) {
        return limit(list, 0, maxSize);
    }

    public static <T> List<T> limit(List<T> list, int offset, int length) {
        if(list == null){
            return Collections.emptyList();
        }
        if(list.isEmpty()){
            return list;
        }
        return list instanceof RandomAccess
                ? new ArrayList<>(list.subList(offset, Math.min(list.size(),offset+length)))
                : list.stream().skip(offset).limit(Math.min(list.size(),length)).collect(Collectors.toList());
    }

    public static  <T,R> Stream<R> flatMap(List<T> list,Function<? super T, ? extends Stream<? extends R>> mapper){
        return stream(list).flatMap(mapper);
    }

    public static  <TO,TI,R> List<R> flatMapList(Collection<TO> outCollection,
                                             Collection<TI> inCollection,
                                             BiFunction<TO, TI,List<R>> mapper){
        List<R> list = new LinkedList<>();
        forEach(outCollection,inCollection,(out,in)->list.addAll(mapper.apply(out,in)));
        return list;
    }

    public static  <T,R> List<R> flatMapList(List<T> list,Function<? super T, ? extends Stream<? extends R>> mapper){
        return flatMap(list,mapper).collect(Collectors.toList());
    }

    public static  <T,R> Set<R> flatMapSet(List<T> list,Function<? super T, ? extends Stream<? extends R>> mapper){
        return flatMap(list,mapper).collect(Collectors.toSet());
    }

    public static <K,VT,VR> Map<K,VR> mapValue(Map<K,VT> map,Function<VT,VR> mapper,boolean ignoreNullValue) {
        if(ContainerUtil.isEmpty(map)){
            return new HashMap<>();
        }
        Map<K,VR> result = map instanceof LinkedHashMap ? new LinkedHashMap<>() : new HashMap<>();
        map.forEach((k,v)-> {
            VR value = mapper.apply(v);
            if(ignoreNullValue && value == null){
                return;
            }
            result.put(k,value);
        });
        return result;
    }

    public static  <T,R> Stream<R> map(Collection<T> collection,Function<? super T, ? extends R> mapper){
        return stream(collection).map(mapper);
    }

    public static  <TO,TI,R> List<R> mapList(Collection<TO> outCollection,
                                           Collection<TI> inCollection,
                                           BiFunction<TO, TI,R> mapper){
        List<R> list = new LinkedList<>();
        forEach(outCollection,inCollection,(out,in)->list.add(mapper.apply(out,in)));
        return list;
    }

    public static  <T,R> List<R> mapList(Collection<T> collection,Function<? super T, ? extends R> mapper){
        return map(collection,mapper).collect(Collectors.toList());
    }

    public static  <T,R> Set<R> mapSet(Collection<T> collection,Function<? super T, ? extends R> mapper){
        return map(collection,mapper).collect(Collectors.toSet());
    }

    public static  <T,R> Stream<R> flatMap(T[] array,Function<? super T, ? extends Stream<? extends R>> mapper){
        return stream(array).flatMap(mapper);
    }

    public static  <T,R> List<R> flatMapList(T[] array,Function<? super T, ? extends Stream<? extends R>> mapper){
        return flatMap(array,mapper).collect(Collectors.toList());
    }

    public static  <T,R> Set<R> flatMapSet(T[] array,Function<? super T, ? extends Stream<? extends R>> mapper){
        return flatMap(array,mapper).collect(Collectors.toSet());
    }

    public static  <T,R> Stream<R> map(T[] array,Function<? super T, ? extends R> mapper){
        return stream(array).map(mapper);
    }

    public static  <T,R> List<R> mapList(T[] array,Function<? super T, ? extends R> mapper){
        return map(array,mapper).collect(Collectors.toList());
    }

    public static  <T,R> Set<R> mapSet(T[] array,Function<? super T, ? extends R> mapper){
        return map(array,mapper).collect(Collectors.toSet());
    }

    public static Map<String,String> toStrMap(Map<?,?> map){
        return toMap(map,Convert::toStr,Convert::toStr);
    }

    public static Map<String,Object> toMap(Map<?,?> map){
        return toMap(map,Convert::toStr, Function.identity());
    }

    public static <TK,TV,K,V> Map<K,V> toMap(Map<TK,TV> map,
                                         Function<? super TK, ? extends K> keyMapper,
                                         Function<? super TV, ? extends V> valueMapper){
        if(map == null){
            return Collections.emptyMap();
        }

        return map.entrySet().stream().collect(Collectors.toMap(entry->keyMapper.apply(entry.getKey()),entry->valueMapper.apply(entry.getValue())));
    }

    public static <T,K,V> Map<K,V> toMap(T[] array,
                                         Function<? super T, ? extends K> keyMapper,
                                         Function<? super T, ? extends V> valueMapper){
        return stream(array).collect(Collectors.toMap(keyMapper,valueMapper));
    }

    public static <T,K,V> Map<K,V> toMap(Iterable<T> iterable,
                                         Function<? super T, ? extends K> keyMapper,
                                         Function<? super T, ? extends V> valueMapper){
        return toMap(iterable,keyMapper,valueMapper,throwingMerger());
    }

    public static <T,K,V> Map<K,V> toMap(Iterable<T> iterable,
                                         Function<? super T, ? extends K> keyMapper,
                                         Function<? super T, ? extends V> valueMapper,
                                         BinaryOperator<V> mergeFunction){
        return toMap(iterable,keyMapper,valueMapper,mergeFunction,HashMap::new);
    }

    public static <T,K,V,M extends Map<K,V>> Map<K,V> toMap(Iterable<T> iterable,
                                                           Function<? super T, ? extends K> keyMapper,
                                                           Function<? super T, ? extends V> valueMapper,
                                                           BinaryOperator<V> mergeFunction,
                                                           Supplier<M> mapSupplier){
        if(iterable == null){
            return Collections.emptyMap();
        }

        return stream(iterable).collect(Collectors.toMap(keyMapper, valueMapper, mergeFunction, mapSupplier));
    }

    public static <T,K,V,M extends Map<K,V>> Map<K,V> toMapSafe(Iterable<T> iterable,
                                                                Function<? super T, ? extends K> keyMapper,
                                                                Function<? super T, ? extends V> valueMapper){
        return toMapSafe(iterable,keyMapper,valueMapper, HashMap::new);
    }

    public static <T,K,V,M extends Map<K,V>> Map<K,V> toMapSafe(Iterable<T> iterable,
                                                                Function<? super T, ? extends K> keyMapper,
                                                                Function<? super T, ? extends V> valueMapper,
                                                                Supplier<M> mapSupplier){
        return toMapSafe(iterable,keyMapper,valueMapper,mapSupplier,null);
    }

    public static <T,K,V,M extends Map<K,V>> Map<K,V> toMapSafe(Iterable<T> iterable,
                                                                Function<? super T, ? extends K> keyMapper,
                                                                Function<? super T, ? extends V> valueMapper,
                                                                Supplier<M> mapSupplier,
                                                                BiFunction<V,V,V> mergeFunction){
        // toMap(iterable,keyMapper,valueMapper,(v1,v2)->v2,mapSupplier); value为null将抛空指针异常 详情看 map.merge方法
        if(iterable == null){
            return Collections.emptyMap();
        }
        Map<K,V> map = mapSupplier.get();
        for (T t : iterable) {
            if(mergeFunction == null){
                map.put(keyMapper.apply(t),valueMapper.apply(t));
            }else {
                K key = keyMapper.apply(t);
                V value = valueMapper.apply(t);
                V oldValue = map.get(key);
                if(oldValue != null){
                    value = mergeFunction.apply(oldValue, value);
                }
                map.put(key,value);
            }
        }
        return map;
    }

    public static List<Long> splitToIds(String str) {
        return splitToIds(str,",");
    }

    public static List<Long> splitToIds(String str, String delimiter) {
        return splitToStream(str,delimiter).map(Convert::toLong).collect(Collectors.toList());
    }

    public static List<Long> splitToIds(List<String> ids){
        return splitToIds(ids,",");
    }

    public static List<Long> splitToIds(List<String> ids,String delimiter){
        if(isEmpty(ids)){
            return new LinkedList<>();
        }
        return ids.stream()
                .flatMap(item->splitToStream(item,delimiter))
                .map(Convert::toLong)
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
    }

    public static List<Long> toIds(List<String> ids){
        if(ids == null){
            return new LinkedList<>();
        }
        return ids.stream().map(Convert::toLong).collect(Collectors.toList());
    }

    public static <T> Set<T> toSet(Collection<T> collection){
        if(collection == null){
            return Collections.emptySet();
        }
        return collection instanceof Set ? (Set<T>) collection : new HashSet<>(collection);
    }

    public static <T> List<T> toList(Object obj){
        if(obj == null){
            return Collections.emptyList();
        }
        if(obj instanceof List){
            //noinspection unchecked
            return (List<T>) obj;
        }
        if(obj instanceof Collection){
            //noinspection unchecked,rawtypes
            return new ArrayList<>((Collection)obj);
        }
        if(obj.getClass().isArray()){
            int length = Array.getLength(obj);
            List<T> items = new ArrayList<>(Math.max(length, 16));
            for (int i = 0; i < length; i++) {
                //noinspection unchecked
                items.add((T) Array.get(obj,i));
            }
            return items;
        }
        ArrayList<Object> list = new ArrayList<>();
        list.add(obj);
        //noinspection unchecked
        return (List<T>) list;
    }

    private static <T> Collection<T> toCollection(Iterable<T> iterable) {
        return iterable instanceof Collection ? (Collection<T>) iterable : IterableUtils.toList(iterable);
    }

    @SafeVarargs
    public static <T> Stream<T> flatOf(List<T> ... arrays) {
        return Stream.of(arrays).filter(Objects::nonNull).flatMap(Collection::stream);
    }

    @SafeVarargs
    public static <T> List<T> flatOfToList(List<T> ... arrays) {
        return flatOf(arrays).collect(Collectors.toList());
    }

    private static <T> BinaryOperator<T> throwingMerger() {
        return (u,v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); };
    }

    @SafeVarargs
    public static <Bean> List<Bean> newLinkedList(Bean ... beans) {
        LinkedList<Bean> list = new LinkedList<>();
        forEach(beans, list::add);
        return list;
    }

    public static <Bean> List<Bean> newSingleList(Bean bean) {
        return Collections.singletonList(bean);
    }

    @SafeVarargs
    public static <Bean> Set<Bean> newHashSet(Bean ... beans) {
        Set<Bean> set = new HashSet<>();
        forEach(beans, set::add);
        return set;
    }

    public static <K,V> Map<K,List<V>> groupBy(Iterable<V> iterable,Function<? super V, ? extends K> classifier) {
        Assert.notNull(classifier,"分组时key生成者不能为空");
        return stream(iterable).collect(Collectors.groupingBy(classifier));
    }

    public static <K,V> Map<K,List<V>> groupBySafe(Iterable<V> iterable,Function<? super V, ? extends K> classifier) {
        Assert.notNull(classifier,"分组时key生成者不能为空");
        if(iterable == null){
            return Collections.emptyMap();
        }
        Map<K,List<V>> result = new HashMap<>();
        for (V info : iterable) {
            K key = classifier.apply(info);
            List<V> vs = result.computeIfAbsent(key, k -> new LinkedList<>());
            vs.add(info);
        }
        return result;
    }

    @SafeVarargs
    public static <V> Map<String,List<V>> groupBySafe(Iterable<V> iterable,
                                                      String delimiter,
                                                      Function<? super V, Object> ... classifiers) {
        Assert.notEmpty(classifiers,"分组时key生成者不能为空");
        if(iterable == null){
            return Collections.emptyMap();
        }

        Function<? super V, String> function = v -> Arrays.stream(classifiers)
                .map(item -> item.apply(v).toString()).collect(Collectors.joining(delimiter));

        return groupBySafe(iterable,function);
    }


    public static <TO,TI> void forEach(Collection<TO> outBeans,
                                       Collection<TI> innerBeans,
                                       BiConsumer<TO,TI> consumer){
        if(ContainerUtil.isEmpty(outBeans) || ContainerUtil.isEmpty(innerBeans)){
            return;
        }

       for (TO outBean : outBeans) {
           for (TI innerBean : innerBeans) {
               consumer.accept(outBean,innerBean);
           }
       }
    }

    public static <T> void forEach(Collection<T> beans, Consumer<? super T> action) {
        if(beans == null){
            return;
        }
        beans.forEach(action);
    }

    public static <T> void forEach(T[] beans, Consumer<? super T> action) {
        if(beans == null){
            return;
        }
        for (T bean : beans) {
            action.accept(bean);
        }
    }

    public static <K,V> void forEach(Map<K,V> beans, BiConsumer<K,V> action) {
        if(beans == null){
            return;
        }

        Set<Map.Entry<K, V>> entries = beans.entrySet();
        for (Map.Entry<K, V> entry : entries) {
            action.accept(entry.getKey(),entry.getValue());
        }
    }

    public static <T> T getLast(List<T> list){
        if(isEmpty(list)){
            return null;
        }
        return list instanceof LinkedList ? ((LinkedList<T>)list).getLast() : list.get(list.size()-1);
    }

    public static <T> T getLastThrow(List<T> list){
        T last = getLast(list);
        if(last == null){
            throw new IllegalArgumentException("getLastThrow() list is null or empty not has element");
        }
        return last;
    }

    public static <T> T getFirst(Iterable<T> iterable){
        if(isEmpty(iterable)){
            return null;
        }
        if(iterable instanceof List){
            List<T> list = (List<T>) iterable;
            return list instanceof LinkedList ? ((LinkedList<T>)iterable).getFirst() : list.get(0);
        }else {
            return iterable.iterator().next();
        }
    }

    public static <T> T getFirstThrow(Iterable<T> iterable){
        T first = getFirst(iterable);
        if(first == null){
            throw new IllegalArgumentException("getFirstThrow() iterable is null or empty not has element");
        }
        return first;
    }

    public static <T> List<T> getRepeatElementList(Collection<T> collection) {
        return getRepeatElementList(collection,false);
    }

    public static <T> List<T> getRepeatElementList(Collection<T> collection,boolean ignoreNull) {
        if(ContainerUtil.isEmpty(collection)){
            return Collections.emptyList();
        }
        Stream<T> stream = collection.stream();
        if(ignoreNull){
            //noinspection ResultOfMethodCallIgnored
            stream.filter(Objects::nonNull);
        }
        Map<T, List<T>> groups = stream.collect(Collectors.groupingBy(item -> item));
        return groups.values().stream()
                .filter(items -> items.size() > 1)
                .map(items->items.get(0))
                .collect(Collectors.toList());
    }

    public static <T,R> List<R> getRepeatElementList(Collection<T> collection,Function<T,R> keySelect) {
        return getRepeatElementList(collection,keySelect,false);
    }

    public static <T,R> List<R> getRepeatElementList(Collection<T> collection,Function<T,R> keySelect,boolean ignoreNull) {
        Set<R> set = handleNull(collection, Collections::emptyList).stream().map(keySelect).collect(Collectors.toSet());
        return getRepeatElementList(set,ignoreNull);
    }

    public static <T> List<T> distance(List<T> list) {
        return stream(list).distinct().collect(Collectors.toList());
    }

    public static <T> List<T> distance(List<T> list,Function<T,String> keyGenerator) {
        return distance(list,keyGenerator,size->new LinkedList<>());
    }

    public static <T> List<T> distance(List<T> list,Function<T,String> keyGenerator,Function<Integer,List<T>> containerGenerate) {
        if(isEmpty(list)){
            return containerGenerate.apply(0);
        }
        Collection<T> values = ContainerUtil.toMapSafe(list, keyGenerator, item -> item).values();
        List<T> result = containerGenerate.apply(values.size());
        result.addAll(values);
        return result;
    }

    public static <T> List<List<T>> split(Collection<T> collection, int len) {
        if(collection == null){
            return Collections.emptyList();
        }
        List<T> list = new ArrayList<>(collection);
        List<List<T>> result = new LinkedList<>();
        int size = collection.size();
        int count = (size+len-1)/len;
        for (int i = 0; i < count; i++) {
            List<T> subList = list.subList(i * len, (Math.min((i + 1) * len, size)));
            result.add(subList);
        }
        return  result;
    }

    public static <T> boolean isSameField(Collection<T> collection, Function<? super T, ?> mapper) {
        return stream(collection).map(mapper).distinct().count() < 2;
    }

    public static <T> boolean isSameFieldAndFieldNotNull(Collection<T> collection, Function<? super T, ?> mapper) {
        Set<?> fields = stream(collection).map(mapper).collect(Collectors.toSet());
        if(fields.size() > 1){
            return false;
        }
        long nullCount = fields.stream().filter(Objects::isNull).count();
        return nullCount == 0;
    }

    public static <T> boolean isSameFieldAndIgnoreNull(Collection<T> collection, Function<? super T, ?> mapper) {
        Set<?> fields = stream(collection).map(mapper).filter(Objects::nonNull).collect(Collectors.toSet());
        return fields.size() < 2;
    }

    public static <T> Map<T,Integer> calculateCount(Iterable<T> iterable){
        return calculateCount(iterable, Function.identity());
    }

    public static <T,R> Map<R,Integer> calculateCount(Iterable<T> iterable,Function<? super T, ? extends R> classifier){
        @SuppressWarnings("rawtypes")
        Map map = groupBySafe(iterable,classifier);
        @SuppressWarnings("UnnecessaryLocalVariable")
        Map<T,Object> group = map;
        for (Map.Entry<T, Object> entry : group.entrySet()) {
            //noinspection unchecked
            entry.setValue(ContainerUtil.size((List)entry.getValue()));
        }
        return map;
    }

    public static <Bean,Id> List<Bean> generateListTree(List<Bean> list,
                                                        Function<Bean,Id> idMapper,
                                                        Function<Bean,Id> pidMapper,
                                                        BiConsumer<Bean,List<Bean>> bindChildrenConsumer){
        Assert.notNull(idMapper,"idMapper不能为null");
        Assert.notNull(pidMapper,"pidMapper不能为null");
        Assert.notNull(bindChildrenConsumer,"bindChildrenConsumer不能为null");

        if(ContainerUtil.isEmpty(list)){
            return list;
        }

        return ContainerUtil.filter(list, bean -> isRootNode(pidMapper.apply(bean)))
                .peek(root -> bindChildrenNodes(root, list, idMapper, pidMapper, bindChildrenConsumer))
                .collect(Collectors.toList());

    }

    private static <Bean,Id> void bindChildrenNodes(Bean root,
                                                    List<Bean> list,
                                                    Function<Bean,Id> idMapper,
                                                    Function<Bean,Id> pidMapper,
                                                    BiConsumer<Bean, List<Bean>> bindChildrenConsumer) {
        List<Bean> childrenNodes = ContainerUtil.filterList(list, bean ->
                Objects.equals(idMapper.apply(root), pidMapper.apply(bean)));
        bindChildrenConsumer.accept(root,childrenNodes);
        childrenNodes.forEach(node->bindChildrenNodes(node,list,idMapper,pidMapper,bindChildrenConsumer));
    }

    private static <Id> boolean isRootNode(Id pid) {
        String idStr = pid == null ? null : pid.toString();
        return StringUtils.isBlank(idStr) || idStr.equals("0") || pid.equals("-1");
    }

    public static List<String> splitToList(String deviceCode) {
        return splitToList(deviceCode, ",");
    }

    @SuppressWarnings("SameParameterValue")
    public static List<String> splitToList(String str, String delimiter) {
        return splitToStream(str, delimiter)
                .collect(Collectors.toList());
    }

    public static Stream<String> splitToStream(String str, String delimiter) {
        if(StringUtils.isBlank(str)){
            return Stream.empty();
        }
        return Arrays.stream(str.split(delimiter))
                .map(String::trim)
                .filter(s -> !StringUtils.isBlank(s));
    }
}
